package com.authine.cloudpivot.ext.utils;

import com.alibaba.cola.dto.RequestContext;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.cola.exception.Exceptions;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.authine.common.util.ObjectMapperUtils;
import com.authine.hermes.app.operation.admin.dto.ErrorCode;
import com.authine.mvp.app.launcher.domain.utills.ApplicationContextUtils;
import com.authine.mvp.app.launcher.dto.bo.response.BO;
import com.authine.mvp.app.launcher.dto.errorcode.ErrorCodeEnum;
import com.authine.mvp.controller.support.LocalDebugAppRouteBean;
import com.authine.mvp.controller.support.LocalDebugHeaderParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.core.env.Environment;
import org.springframework.http.*;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName HttpClients
 * @Description: 服务之间调用的工具类
 * @Author fj
 * @Date 2021/4/23
 * @Version V1.0
 **/
@Slf4j
public class HttpClients {
    private static final DiscoveryClient discoveryClient = ApplicationContextUtils.getBean(DiscoveryClient.class);
    private static final RestTemplate lbRestTemplate = ApplicationContextUtils.getBean(RestTemplate.class);
    public static final String INSTANCE_NAME_PREFIX = "app-launcher-";

    public static final String DEFAULT_ENCODING = "UTF-8";


    public static <T> T execute(String url, Map<String, Object> paramMap, Class<T> classType) {
        return execute(url, HttpMethod.POST, paramMap, classType);
    }

    /*public static String execute(String url, Map<String, Object> paramMap) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("corpLoginId", RequestContext.getCorpLoginId());
        headers.add("loginId", RequestContext.getLoginId());
        headers.add("loginName", RequestContext.getLoginName());
        return execute(url, HttpMethod.POST, paramMap, null, headers);
    }*/

    public static String execute(String url, Object paramMap) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("corpLoginId", RequestContext.getCorpLoginId());
        headers.add("loginId", RequestContext.getLoginId());
        headers.add("loginName", RequestContext.getLoginName());
        return execute(url, HttpMethod.POST, paramMap, null, headers);
    }

    public static String execute(String url, Object paramMap,HttpMethod method) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("corpLoginId", RequestContext.getCorpLoginId());
        headers.add("loginId", RequestContext.getLoginId());
        headers.add("loginName", RequestContext.getLoginName());
        return execute(url, method, paramMap, null, headers);
    }

    public static <T> T execute(String url, HttpMethod method, Object paramMap, Class<T> classType) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("corpLoginId", RequestContext.getCorpLoginId());
        headers.add("loginId", RequestContext.getLoginId());
        headers.add("loginName", RequestContext.getLoginName());
        String body = execute(url, method, paramMap, null, headers);
        return ObjectMapperUtils.fromJSON(body, classType);
    }


    public static <T> T post(String url, Object paramMap, Class<T> classType) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("corpLoginId", RequestContext.getCorpLoginId());
        headers.add("loginId", RequestContext.getLoginId());
        headers.add("loginName", RequestContext.getLoginName());
        String body = execute(url, HttpMethod.POST, paramMap, null, headers);
        return ObjectMapperUtils.fromJSON(body, classType);
    }


    /*public static String execute(String url, HttpMethod method, Map<String, Object> paramMap, MultiValueMap<String, String> query, HttpHeaders headers) {
        return execute(url, method, paramMap, query, headers);
    }*/

    public static String execute(String url, HttpMethod method, Object paramObj, MultiValueMap<String, String> query, HttpHeaders headers) {
        headers.setContentType(MediaType.APPLICATION_JSON);
        ResponseEntity<String> responseEntity = null;
        try {
            switch (method) {
                case DELETE:
                case GET:
                    log.info("" + method + "请求参数：{},url:{}", query, url);
                    HttpEntity<Object> requestEntity = new HttpEntity<>(query, headers);
                    responseEntity = lbRestTemplate.exchange(url, method, requestEntity, String.class);
                    log.info("" + method + "请求结果：{}", responseEntity);
                    break;
                case POST:
                    HttpEntity<String> request = new HttpEntity<>(JSON.toJSONString(paramObj), headers);
                    UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url).queryParams(query);
                    log.info("POST请求参数：{},url:{}", JSON.toJSONString(paramObj), builder.toUriString());
                    responseEntity = lbRestTemplate.exchange(builder.toUriString(), method, request, String.class);
                    log.info("POST请求结果：{}", responseEntity);
                    break;
                default:
                    break;
            }
            if (responseEntity == null) {
                Exceptions.throwBizException(ErrorCodeEnum.CUSTOM_SERVICE_EXCEPTION);
            }
        } catch (RestClientException e) {
            log.error("BO_APPLICATION_SERVICE_EXCEPTION", e);
            Exceptions.throwBizException(ErrorCodeEnum.BO_APPLICATION_SERVICE_EXCEPTION);
        }
        return responseEntity.getBody();
    }

    public static String getUrl(String appCode, String suffixUrl) {
//        Assert.isTrue(!StringUtils.isEmpty(appCode), "appCode不能为空！");
//        String instanceName = INSTANCE_NAME_PREFIX.concat(appCode);
//        String prefixUrl = discoveryClient.getInstances(instanceName).stream().findFirst()
//                .map(ServiceInstance::getUri)
//                .map(String::valueOf)
//                .orElseThrow(() -> new IllegalArgumentException(instanceName.concat("实例不存在")));
//        return prefixUrl.concat(suffixUrl);
        String url = getUrl(appCode);
        if (StringUtils.isEmpty(url)) {
            Exceptions.throwBizException(ErrorCode.APP_NOT_RUNNING);
        }
        return url.concat(suffixUrl);
    }

    public static String getBaseUrl(String appCode, String suffixUrl) {
        String baseUrl = discoveryClient.getInstances(appCode).stream().findFirst()
                .map(ServiceInstance::getUri).map(String::valueOf).orElseThrow(() -> new IllegalArgumentException(appCode.concat("实例不存在")));
        return baseUrl.concat(suffixUrl);
    }

    public static String getUrl(String appCode) {
        Environment environment = ApplicationContextUtils.getBean(Environment.class);
        String property = environment.getProperty("debugApplicationService");
        if (StringUtils.isEmpty(property) || "false".equals(property)) {
            LocalDebugAppRouteBean localDebugAppRouteBean = ApplicationContextUtils.getBean(LocalDebugAppRouteBean.class);
            return localDebugAppRouteBean.getBaseUrl(appCode, LocalDebugHeaderParam.builder()
                    .requestId(RequestContext.getRequestId()).loginName(RequestContext.getLoginName())
                    .requestId(RequestContext.getRequestId()).build());
        }
        DiscoveryClient discoveryClient = ApplicationContextUtils.getBean(DiscoveryClient.class);
        List<ServiceInstance> instances = discoveryClient.getInstances("app-launcher-" + appCode);
        if (CollectionUtils.isEmpty(instances)) {
            return null;
        }
        for (ServiceInstance instance : instances) {
            Map<String, String> metadata = instance.getMetadata();
            if (StringUtils.isEmpty(metadata.get("nodePort"))) {
                continue;
            }
            log.info("metadata:{}", JSONObject.toJSONString(metadata));
            String nodeIp = metadata.get("nodeIp");
            String nodePortStr = metadata.get("nodePort");
            return new DefaultServiceInstance(instance.getInstanceId(), instance.getServiceId(), nodeIp, Integer.parseInt(nodePortStr), false).getUri().toString();
        }
        return null;
    }

    public static final String listBo = "/application/invoke?action=/ApplicationService/listBO";
    public static final String loadBo = "/application/invoke?action=/ApplicationService/loadBO";


    /**
     * 调用其他应用的系统方法 /ApplicationService/createBO
     *
     * @param AppCode    应用编码
     * @param schemaCode 模型编码
     * @param data       请求数据
     * @return 响应结果
     */
    public static SingleResponse<BO> createBO(String AppCode, String schemaCode, List<?> data) {
        Map<String, Object> saveMap = new HashMap<>();
        saveMap.put("schemaCode", schemaCode);
        saveMap.put("data", data);
        String createBO = "/application/invoke?action=/ApplicationService/createBO";
        String execute = HttpClients.execute(HttpClients.getUrl(AppCode, createBO), saveMap);

        return TypeReferenceUtils.readValueToSingleResponse(execute);
    }


    /**
     * 构建http请求头
     *
     * @param appCode 应用编码
     * @return 请求头
     */
    public static HttpHeaders buildHttpHeaders(String appCode) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("corpLoginId", RequestContext.getCorpLoginId());
        headers.add("loginId", RequestContext.getLoginId());
        headers.add("loginName", RequestContext.getLoginName());
        headers.add("rpcSourceAppCode", appCode);
        return headers;
    }

    /**
     * 振德请求地址
     *
     * @param xml    请求参数
     * @param url    请求路径
     * @param params 请求参数
     * @throws Exception
     */
    public static String sendZhenSyncRequest(String xml, String url, Map<String, String> params) throws Exception {
        HttpURLConnection httpConn;
        BufferedReader reader;

        httpConn = (HttpURLConnection) new URL(url).openConnection();
        httpConn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
        httpConn.setRequestProperty("SOAPAction", params.get("SOAPAction"));
        httpConn.setRequestProperty("Content-Length", String.valueOf(xml.length()));
        httpConn.setRequestProperty("Connection", params.get("Connection"));
        httpConn.setRequestProperty("User-Agent", params.get("User-Agent"));
        httpConn.setRequestProperty("Cookie", params.get("Cookie"));
        httpConn.setRequestProperty("Cookie2", params.get("Cookie2"));
        httpConn.setRequestProperty("Authorization", params.get("Authorization"));
        /*httpConn.setRequestProperty("Username", params.get("Username"));
        httpConn.setRequestProperty("Password", params.get("Password"));*/
        httpConn.setRequestMethod("POST");
        httpConn.setDoOutput(true);
        httpConn.setDoInput(true);
        httpConn.connect();

        // 将要提交服务器的SOAP请求字符流写入输出流
        httpConn.getOutputStream().write(xml.getBytes(DEFAULT_ENCODING));
        // 用来获取服务器响应状态
        int responseCode = httpConn.getResponseCode();
        String tempString = null;
        StringBuffer retMsg = new StringBuffer();

        log.warn("振德 -- responseCode：" + responseCode);

        if (responseCode == HttpURLConnection.HTTP_OK) {

            reader = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), DEFAULT_ENCODING));

            while ((tempString = reader.readLine()) != null) {
                retMsg.append(tempString);
            }
            //关闭流
            reader.close();
            log.debug("response success--->{}", retMsg.toString());
        } else {
            log.debug("response code--->{}", httpConn.getResponseCode());
        }
        // 响应报文
        return retMsg.toString();
    }

    /**
     * pojo转换成xml
     *
     * @param obj 待转化的对象
     * @return xml格式字符串
     * @throws Exception JAXBException
     */
    public static String convertToXml(Object obj) throws Exception {
        String result;

        JAXBContext context = JAXBContext.newInstance(obj.getClass());
        Marshaller marshaller = context.createMarshaller();
        // 指定是否使用换行和缩排对已编组 XML 数据进行格式化的属性名称。
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, DEFAULT_ENCODING);

        StringWriter writer = new StringWriter();
        marshaller.marshal(obj, writer);
        result = writer.toString();

        return result;
    }
}
